"use strict";

const MESSAGE_TYPE_ANSWER = 0x02;
const MESSAGE_TYPE_CANDIDATE = 0x03;
const MESSAGE_TYPE_HANGUP = 0x04;

const SIGNAL_TYPE_JOIN = "join";                // 创建房间 或者 加入到现有房间
const SIGNAL_TYPE_NEW_PEER = "new-peer";        // 成功加入到现有房间后，通知房间中的其他人谁加入了
const SIGNAL_TYPE_RESP_JOIN = "resp-join";      // 成功加入到现有房间后，通知自己房间中有哪些人
const SIGNAL_TYPE_OFFER = "offer";              // 发送offer给对端
const SIGNAL_TYPE_ANSWER = "answer";            // 发送answer给对端
const SIGNAL_TYPE_CANDIDATE = "candidate";      // 发送candidate给对端
const SIGNAL_TYPE_LEAVE = "leave";              // 告诉服务器自己要离开房间
const SIGNAL_TYPE_PEER_LEAVE = "peer-leave";    // 离开房间后，通知房间中的其他人

var txtRoomId = document.getElementById("txtRoomId");
var localVideo = document.querySelector("#localVideo");
var remoteVideo = document.querySelector("#remoteVideo");

var signalClient; // 信令客户端（只是对websocket客户端简单的封装）

var roomId = "100";
var userId = new Date().getTime().toString(36); // 时间戳转三十六进制方式生成唯一编号，如：lswte2aq
var remoteUserId = -1;
var localStream = null; // 本地码流
var remoteStream = null; // 远端码流
var pc = null; // PeerConnection 对象

//#region WebSocket
var SignalClient = function (wsUrl) {
    this.init(wsUrl);
    signalClient = this;
    return this;
};

SignalClient.prototype.init = function (wsUrl) {
    // websocket 服务器 url
    this.wsUrl = wsUrl;
    // websocket 客户端对象
    this.ws = null;
};

SignalClient.prototype.createWebSocket = function () {
    signalClient = this;
    signalClient.ws = new WebSocket(this.wsUrl);

    signalClient.ws.onopen = (ev) => {
        console.log(`[websocket onopen]`);
        signalClient.onOpen(ev);
    };

    signalClient.ws.onmessage = (ev) => {
        console.log(`[websocket onmessage] ${ev.data}`);
        signalClient.onMessage(ev);
    };

    signalClient.ws.onerror = (ev) => {
        console.log(`[websocket onerror] ${ev.data}`);
        signalClient.onError(ev);
    };

    signalClient.ws.onclose = (ev) => {
        console.log(`[websocket onclose] code:${ev.code}, reason:${ev.reason}`);
        signalClient.onClose(ev);
    };
};

SignalClient.prototype.onOpen = function (ev) { };

SignalClient.prototype.onMessage = function (ev) {
    let message = JSON.parse(ev.data);
    switch (message.cmd) {
        case SIGNAL_TYPE_NEW_PEER:
            receivedNewPeer(message);
            break;
        case SIGNAL_TYPE_RESP_JOIN:
            receivedRespJoin(message);
            break;
        case SIGNAL_TYPE_OFFER:
            receivedOffer(message);
            break;
        case SIGNAL_TYPE_ANSWER:
            receivedAnswer(message);
            break;
        case SIGNAL_TYPE_CANDIDATE:
            receivedCandidate(message);
            break;
        case SIGNAL_TYPE_PEER_LEAVE:
            receivedPeerLeave(message);
            break;
    }
};

SignalClient.prototype.onError = function (ev) { };

SignalClient.prototype.onClose = function (ev) { };

SignalClient.prototype.send = function (message) {
    console.log(`[websocket send] ${message}`);
    this.ws.send(message);
};

signalClient = new SignalClient(
    `ws://192.168.2.156:8089/${roomId}/${userId}`
);
signalClient.createWebSocket();
//#endregion

//#region 收到信令服务器消息后的处理
function receivedNewPeer(message) {
    // 其他人加入房间后发来的通知
    console.log(`-->receivedNewPeer. message: ${message}`);

    remoteUserId = message.remoteUserId;
    createOffer();
}

function receivedRespJoin(message) {
    // 自己成功加入现有房间
    console.log(`-->receivedRespJoin. message: ${message}`);

    remoteUserId = message.remoteUserId;
}

function receivedOffer(message) {
    // 收到其他人的offer
    console.log(`-->receivedOffer. message: ${message}`);

    if (pc == null) {
        createPeerConnection();
    }

    let options = {
        type: message.cmd,
        sdp: message.sdp
    }

    let offer = new RTCSessionDescription(options);
    pc.setRemoteDescription(offer);
    createAnswer();
}

function receivedAnswer(message) {
    // 收到其他人的answer
    console.log(`-->receivedAnswer. message: ${message}`);

    let options = {
        type: message.cmd,
        sdp: message.sdp
    }

    let answer = new RTCSessionDescription(options);
    pc.setRemoteDescription(answer);
}

function receivedCandidate(message) {
    // 收到其他人的candidate
    console.log(`-->receivedCandidate. message: ${message}`);

    let options = {
        sdpMLineIndex: message.sdpMLineIndex,
        sdpMid: message.sdpMid,
        candidate: message.candidate
    }

    let candidate = new RTCIceCandidate(options);
    pc.addIceCandidate(candidate).catch((error) => {
        console.error(`-->receivedCandidate failed: ${error}`);
    });
}

function receivedPeerLeave(message) {
    // 其他人离开房间
    console.log(`-->receivedPeerLeave. message: ${message}`);

    remoteUserId = message.remoteUserId;
}
//#endregion

function createPeerConnection() {
    console.log(`-->createPeerConnection.`);

    try {
        // 创建 PeerConnection 实例
        pc = new RTCPeerConnection(null);

        // 获取到candidate后的处理
        pc.onicecandidate = (event) => {
            console.log(`[webrtc peerconnection onicecandidate]`);

            if (event.candidate) {
                let jMsg = {
                    cmd: SIGNAL_TYPE_CANDIDATE,
                    roomId: roomId,
                    userId: userId,
                    remoteUserId: remoteUserId,
                    sdpMid: event.candidate.sdpMid,
                    sdpMLineIndex: event.candidate.sdpMLineIndex,
                    candidate: event.candidate.candidate
                };

                let msg = JSON.stringify(jMsg);
                signalClient.send(msg);
            } else {
                // 无法实现内网穿透，不使用穿透
                console.log(`-->createPeerConnection. End of candidate.`);
            }
        };

        // 获取到远端码流后的处理
        pc.ontrack = (event) => {
            console.log(`[webrtc peerconnection ontrack]`);

            // 将远端码流渲染到界面
            remoteStream = event.streams[0];
            remoteVideo.srcObject = remoteStream;
        };

        // 远端码流删除后的处理
        pc.onremovestream = () => {
            console.log(`[webrtc peerconnection onremovestream]`);
        };

        // SDP信令状态改变后的处理
        pc.onsignalingstatechange = () => {
            console.log(`[webrtc peerconnection onsignalingstatechange]`);
        };

        // peer连接状态改变后的处理
        pc.onconnectionstatechange = () => {
            console.log(`[webrtc peerconnection onconnectionstatechange]`);
        };

        // ICE连接状态改变后的处理
        pc.oniceconnectionstatechange = () => {
            console.log(`[webrtc peerconnection oniceconnectionstatechange]`);
        };

        // 将本地视频流添加到 PeerConnection 实例中
        // 后加入的一端，会报错，可能是 getUserMedia() 用的是 promise 方式，异步导致 localStream 还未初始化。
        // 或者是因为两个端在同一浏览器打开导致。两个端如果在同一电脑不同浏览器打开，会有相机权限的问题。等部署好再测试
        if (localStream != null) {
            //todo: 处理好上面的错误后，删除 localStream != null 的判断
            localStream.getTracks().forEach((track) => {
                pc.addTrack(track, localStream);
            });
            console.log(`**localStream.getTracks()`);
        }

        //pc.addStream(localStream);

        console.log(`-->createPeerConnection. PeerConnection Created`);
    } catch (e) {
        console.error(
            `-->createPeerConnection. Failed to create PeerConnection, error: ${e.message}`
        );
        alert("Can not create PeerConnection");
        return;
    }
}

function createOffer() {
    // 创建 PeerConnection 对象
    if (pc == null) {
        createPeerConnection();
    }

    let jArg = {
        //同时接收远端的音、视频数据
        offerToRecieveAudio: 1,
        offerToRecieveVideo: 1,
    };

    pc.createOffer(jArg)
        .then(sendOffer)
        .catch((error) => {
            console.log(`-->createOffer failed: ${error}`);
        });
}

function createAnswer() {
    pc.createAnswer()
        .then(sendAnswer)
        .catch((error) => {
            console.log(`-->createAnswer failed: ${error}`);
        });
}

function sendOffer(session) {
    pc.setLocalDescription(session)
        .then(() => {
            let jMsg = {
                cmd: SIGNAL_TYPE_OFFER,
                roomId: roomId,
                userId: userId,
                remoteUserId: remoteUserId,
                sdp: session.sdp,
            };
            let msg = JSON.stringify(jMsg);
            signalClient.send(msg);
        })
        .catch((error) => {
            console.error(`-->sendOffer setLocalDescription faild: ${error}`);
        });
}

function sendAnswer(session) {
    pc.setLocalDescription(session)
        .then(() => {
            let jMsg = {
                cmd: SIGNAL_TYPE_ANSWER,
                roomId: roomId,
                userId: userId,
                remoteUserId: remoteUserId,
                sdp: session.sdp,
            };
            let msg = JSON.stringify(jMsg);
            signalClient.send(msg);
        })
        .catch((error) => {
            console.error(`answer setLocalDescription faild. error: ${error}`);
        });
}

// 加入按钮点击后的处理
document.getElementById("btnJoin").onclick = () => {
    console.log("-->btnJoin onclick");

    // 打开摄像头并初始化本地码流
    navigator.mediaDevices
        .getUserMedia({
            audio: true,
            video: true,
        })
        .then((stream) => {
            localVideo.srcObject = stream;
            localStream = stream;
        })
        .catch((e) => {
            console.error(`-->btnJoin onclick error:${e}`);
        });

    // 发送消息
    let jMsg = {
        cmd: SIGNAL_TYPE_JOIN,
        roomId: roomId,
        userId: userId,
    };
    signalClient.send(JSON.stringify(jMsg));
};

// 离开按钮点击后的处理
document.getElementById("btnLeave").onclick = () => {
    console.log("-->btnLeave onclick");

    let jMsg = {
        cmd: SIGNAL_TYPE_LEAVE,
        roomId: roomId,
        userId: userId,
    };
    signalClient.send(JSON.stringify(jMsg));

    remoteVideo.srcObject = null;
};
